這兩天我希望研究在 Unity 中控制 Camera 的方式,主要是我們通常在環境中視角幾乎都是讓Camera 移動來讓我們觀看環鏡中的位置。有時候我們會希望Camera 能跟隨物件、旋轉Camera 自身、根據與物件的距離產生不同的移動速度、鏡頭的拉伸、切換不同鏡頭等等。所以我希望在這兩天好好的熟悉一下使用Unity Camera ,並把這個苦惱我很久的鏡頭控制給釐清,未來或許要常使用。拆成兩天是因為可能這次的學習量很大故拆成兩天。
在 Hierarchy 中新增之前使用的小雞 Prefab。
接下來在Main Camera的身上新增一個文本,取名為 CameraControl1.cs
# region Fixed Camera
public Transform chickenModel;
float offsetZ = 5f, offsetY = 1f;
void Start()
{
transform.rotation = Quaternion.identity;
Vector3 pos = chickenModel.position;
pos.z -= offsetZ;
transform.position = pos;
}
# endregion
程式中 transform 為該Camera 的位置,offsetZ , offsetY 是 Camera 與物件的距離,首先旋轉 Rotation 歸零,Camera的位置是由鎖定物件上的位置基礎進行減去或增加默認的 offset 數值,這邊要鎖定在公雞後方與稍微上面一點俯瞰該公雞。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl1 : MonoBehaviour
{
# region Fixed Camera
public Transform chickenModel;
float offsetZ = 5f, offsetY = 1f;
void Start()
{
transform.rotation = Quaternion.identity;
Vector3 pos = chickenModel.position;
pos.z -= offsetZ;
transform.position = pos;
}
# endregion
#region Rotate Camera
void Update()
{
float mouse_dx = Input.GetAxis("Mouse X");
float mouse_dy = Input.GetAxis("Mouse Y");
if(Input.GetMouseButton(1))
{
if(Mathf.Abs(mouse_dx) > 0 || Mathf.Abs(mouse_dy) > 0)
{
// get the camera eular angle
Vector3 currentCameraAngle = transform.rotation.eulerAngles;
currentCameraAngle.x = Mathf.Repeat(currentCameraAngle.x + 180f, 360f) -180f; // always true 0 ~ 180
currentCameraAngle.y += mouse_dx; // unity yaw Y, right is positive, left i negative
currentCameraAngle.x -= mouse_dy; // unity pitch X, up is negative, down is positive
Quaternion cameraRotation = Quaternion.identity;
cameraRotation.eulerAngles = new Vector3(currentCameraAngle.x , currentCameraAngle.y , 0);
transform.rotation = cameraRotation;
}
}
}
#endregion
}
關於 Rotate 這邊的說明,我想先釐清一個觀念就是,在 Unity 中環境的坐標系為左手坐標系,所以說當旋轉物體中 Rotation 座標 X 的時候為 pitch,旋轉 Y 軸時為 yaw, 旋轉 Z 軸時為 roll 。因此當我們取得該滑鼠的X, Y 軸移動時的數值,請記得滑鼠X軸移動對應到 yaw,故為 Unity 的 Y軸,右正左負。滑鼠 Y軸移動對應到 pitch,故為 Unity 的 X軸,上負下正。這邊的觀念很容易搞混,所以要先有先輩知識會比較好理解。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl1 : MonoBehaviour
{
# region Fixed Camera
public Transform chickenModel;
float offsetZ = 2f, offsetY = 1f;
void Start()
{
transform.rotation = Quaternion.identity;
Vector3 pos = chickenModel.position;
pos.z -= offsetZ;
transform.position = pos;
}
# endregion
#region Rotate Camera
void Update()
{
float mouse_dx = Input.GetAxis("Mouse X");
float mouse_dy = Input.GetAxis("Mouse Y");
if(Input.GetMouseButton(1))
{
if(Mathf.Abs(mouse_dx) > 0 || Mathf.Abs(mouse_dy) > 0)
{
// get the camera eular angle
Vector3 currentCameraAngle = transform.rotation.eulerAngles;
currentCameraAngle.x = Mathf.Repeat(currentCameraAngle.x + 180f, 360f) -180f; // always true 0 ~ 180
currentCameraAngle.y += mouse_dx; // unity yaw Y, right is positive, left i negative
currentCameraAngle.x -= mouse_dy; // unity pitch X, up is negative, down is positive
Quaternion cameraRotation = Quaternion.identity;
cameraRotation.eulerAngles = new Vector3(currentCameraAngle.x , currentCameraAngle.y , 0);
transform.rotation = cameraRotation;
// reset the camera position
Vector3 modelPosition = chickenModel.position;
Vector3 distance = cameraRotation * new Vector3(0f, 0f, offsetZ);
transform.position = modelPosition - distance;
}
}
}
#endregion
}
最後添加的這邊要注意到若要鎖定在旋轉的時候持續注視某一物件,該物件(modelPosition)的位置取得後,將Camera 的旋轉角度,也就是旋轉方向,乘上一個向量,也就是我們的 new Vector3(0f, 0f ,offsetZ)後再作相減。該旋轉方向和向量相乘也就是相當於在後退了 offsetZ 的距離,接著進行旋轉。 角度乘上向量會有距離,有物件的距離後再減去該方向上的距離(distance) 就會是該 Camera 的位置。
Vector3 modelPosition = chickenModel.position;
Vector3 distance = cameraRotation * new Vector3(0f, 0f, offsetZ);
transform.position = modelPosition - distance;
Vector3 currentCameraAngle = transform.rotation.eulerAngles;
cameraRotation.eulerAngles = new Vector3(currentCameraAngle.x , currentCameraAngle.y , 0);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl1 : MonoBehaviour
{
# region Fixed Camera
public Transform chickenModel;
float offsetZ = 2f, offsetY = 1f;
public Vector3 getPosition; // store the position
private Quaternion getRotation; // store the rotation
void Start()
{
transform.rotation = Quaternion.identity;
Vector3 pos = chickenModel.position;
pos.z -= offsetZ;
transform.position = pos;
getPosition = chickenModel.position;
}
# endregion
#region Rotate Camera
void Update()
{
float mouse_dx = Input.GetAxis("Mouse X");
float mouse_dy = Input.GetAxis("Mouse Y");
if(Input.GetMouseButton(1))
{
if(Mathf.Abs(mouse_dx) > 0 || Mathf.Abs(mouse_dy) > 0)
{
// get the camera eular angle
Vector3 currentCameraAngle = transform.rotation.eulerAngles;
currentCameraAngle.x = Mathf.Repeat(currentCameraAngle.x + 180f, 360f) -180f; // always true 0 ~ 180
currentCameraAngle.y += mouse_dx; // unity yaw Y, right is positive, left i negative
currentCameraAngle.x -= mouse_dy; // unity pitch X, up is negative, down is positive
Quaternion cameraRotation = Quaternion.identity;
getRotation.eulerAngles = new Vector3(currentCameraAngle.x , currentCameraAngle.y , 0); // count the camera rotation
}
}
}
void FixedUpdate() {
transform.rotation = getRotation; // set the camera rotation
transform.position = getPosition - getRotation * new Vector3(0, 0, offsetZ); // set the position
}
#endregion
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl1 : MonoBehaviour
{
# region Fixed Camera
public Transform chickenModel;
float offsetZ = 2f, offsetY = 1f;
public Vector3 getPosition; // store the position
private Quaternion rotationSlerp, getRotation; // store the rotation
public float rotateSpeed = 32f, rotateLerp = 8f;
void Start()
{
transform.rotation = Quaternion.identity;
Vector3 pos = chickenModel.position;
pos.z -= offsetZ;
transform.position = pos;
getPosition = chickenModel.position;
}
# endregion
#region Rotate Camera
void Update()
{
float mouse_dx = Input.GetAxis("Mouse X");
float mouse_dy = Input.GetAxis("Mouse Y");
if(Input.GetMouseButton(1))
{
mouse_dx *= rotateSpeed;
mouse_dy *= rotateSpeed;
if(Mathf.Abs(mouse_dx) > 0 || Mathf.Abs(mouse_dy) > 0)
{
// get the camera eular angle
Vector3 currentCameraAngle = transform.rotation.eulerAngles;
currentCameraAngle.x = Mathf.Repeat(currentCameraAngle.x + 180f, 360f) -180f; // always true 0 ~ 180
currentCameraAngle.y += mouse_dx; // unity yaw Y, right is positive, left i negative
currentCameraAngle.x -= mouse_dy; // unity pitch X, up is negative, down is positive
getRotation.eulerAngles = new Vector3(currentCameraAngle.x , currentCameraAngle.y , 0);
}
}
}
void FixedUpdate() {
rotationSlerp = Quaternion.Slerp(rotationSlerp, getRotation, Time.deltaTime * rotateLerp); // use Slerp to calculate
transform.rotation = rotationSlerp;
transform.position = getPosition - rotationSlerp * new Vector3(0, 0, offsetZ);
}
#endregion
}